/*
 * @Author: 0x9DEFA478
 * @Date: 2022-01-27 15:59:18
 * @LastEditTime: 2022-03-06 20:13:15
 * @LastEditors: 0x9DEFA478
 * @Description: 
 * QQ:2652450237
 * ============================================================================================================================================
 * 
 * 
 * 
 *                                                                                               ************      ****************************
 *                                                                                             ************      ****************************  
 *                                                                                           ************      ****************************    
 *                                                                                         ************      ****************************      
 *                                                                                       ************      ************                        
 *                                                                                     ************      ************                          
 *                                                                                   ************      ************                            
 *                                                                                 ************      ************                              
 *                                                                               ************      ************                                
 *                                                                             ************      ************                                  
 *                                                                           ************      ************                                    
 *                                                                         ************      ************                                      
 *                                                                       ************      ************                                        
 *                                                                     ************      ************                                          
 *                                                                   ************      ************                                            
 *                                                                 ************      ************                                              
 *                                                               ************      ************                                                
 *                                                             ************      ************                                                  
 *                                                           ************      ************                                                    
 *                                                         ************      ************                                                      
 *                                                       ************      ************                                                        
 *                                                     ************      ************                                                          
 *                                                   ************      ************                                                            
 *                                                 ************      ************                                                              
 *                                               ************      ************                                                                
 *                                             ************      ************                                                                  
 *                                           ************      ************                                                                    
 *                                         ************      ************                                                                      
 *                                       ************      ************                                                                        
 *                                     ************      ************                                                                          
 *                                   ************      ************                                                                            
 *                                 ************      ************                                                                              
 *                               ************      ************                                                                                
 *                             ************      ************                                                                                  
 *                           ************      ************                                                                                    
 *                         ************      ************                                                                                      
 *       ****************************      ************                                                                                        
 *     ****************************      ************                                                                                          
 *   ****************************      ************                                                                                            
 * ****************************      ************                                                                                              
 * 
 * 
 * 
 * ============================================================================================================================================
 * 
 */
#include "nes.h"
#include "H_NES.h"
#include "Peripheral.h"

#include "Console/H_NES_Console.h"


typedef struct{
  void* nes;
  int IsLoad;
  H_TS_Mailbox* Command;
}NES;

H_NES_Console* Dbg_TestConsole;

static NES nes;

static unsigned short Palette[64];


static void* new_MessageQueue(int QueueLength,int Size){
  return new_H_TS_Mailbox(QueueLength+1,Size,1);
}
static void delete_MessageQueue(void* MessageQueue){
  delete_H_TS_Mailbox(MessageQueue);
}
static int MessageSned(void* MessageQueue,void* p){
  return H_TS_MailboxPost(MessageQueue,p);
}
static void MessageReceive(void* MessageQueue,void* p){
  H_TS_MailboxReceive(MessageQueue,p);
}
static int MessageTryReceive(void* MessageQueue,void* p){
  return H_TS_MailboxTryReceive(MessageQueue,p);
}



static void ExitCallback(void* v){

  // extern Hbyte instruct_isUse[];

  // int i;
  // for(i=0;i<256;i++){
  //   H_Printf("\r\n{X2}:{i}:{i}",i,instruct_isUse[i],i);
  // }
  H_Printf("\r\ntimsEnd:{i}\r\n",Time_GetTimeMs());

  //IP_SAI_MasterTx_Stop(ip_sai2_b);
}


static int nes_thread(void* v){

  H_Printf("\r\ntimsStart:{i}\r\n",Time_GetTimeMs());
  H_NES_Start(((NES*)v)->nes);

  return 0;
}


#include "stm32xxx_MemoryMacro.h"

Local_IRAM_AXI __attribute__((aligned(16))) static unsigned short Mem[256*240*2];
static int IsOdd;
static H_TS_BinarySemaphore* DisplayDone;
static H_TS_MessageQueue* DisplayQueue;

static int DisplayThread(void* v){
  void* v0;
  void* v1;

  for(;;){
    H_TS_MessageQueueReceive(DisplayQueue,&v0,&v1);
    ST7789_Display(Screen_ST7789_SPI,v0);
    H_TS_BinarySemaphoreGive(v1);
  }
}

static Hint32 volatile NES_Frame=0;


static void RendersLine(void* v,Hbyte* ColorIndexArray,unsigned int line){
  unsigned short* p;
  int i;

  if(IsOdd==0){

    p=&Mem[line*256];

    for(i=0;i<256;i++){
      p[i]=Palette[ColorIndexArray[i]];
    }

    if(line==239){
      IsOdd=-1;

      H_TS_BinarySemaphoreTake(DisplayDone);
      H_TS_MessageQueueSend(DisplayQueue,&Mem[0],DisplayDone);
      NES_Frame++;
    }

  }else{

    p=&Mem[256*240+line*256];

    for(i=0;i<256;i++){
      p[i]=Palette[ColorIndexArray[i]];
    }

    if(line==239){
      IsOdd=0;

      H_TS_BinarySemaphoreTake(DisplayDone);
      H_TS_MessageQueueSend(DisplayQueue,&Mem[256*240],DisplayDone);
      NES_Frame++;
    }

  }
}


#define vAudioBuffer_Cnt                                          8
Local_IRAM_D2 static Hint32 AudioBufferEmpty[256*2];
Local_IRAM_D2 static Hint32 AudioBuffer[256*2*vAudioBuffer_Cnt];
static volatile int InIndex;
static volatile int OutIndex;
H_TS_BinarySemaphore* Sem_AudioBufferEmpty;

volatile int Audio_Volume=8;


static void* Audio_GetBuffer_Callback(void* v,int IsISR){
  int in;
  int out;

  System_DataMemoryBarrier();

  if(IsISR==0){
    H_TS_BinarySemaphoreGive(Sem_AudioBufferEmpty);
  }else{
    H_TS_BinarySemaphoreGive_ISR(Sem_AudioBufferEmpty);
  }

  in=InIndex;
  out=OutIndex;
  if(in!=out){
    if(out==(vAudioBuffer_Cnt-1)){
      OutIndex=0;
    }else{
      OutIndex=out+1;
    }

    return &AudioBuffer[256*2*out];
  }
  return AudioBufferEmpty;
}

static void AudioOutput(void* v,void* Samples,int Length){
  int in;
  int out;
  int Empty;
  volatile Hint32* Buffer;
  int i;
  int vol;

Start:
  in=InIndex;
  out=OutIndex;

  if(in<out){
    Empty=out-in;
  }else{
    Empty=vAudioBuffer_Cnt-in+out;
  }

  if(Empty<3){
    H_TS_BinarySemaphoreTake(Sem_AudioBufferEmpty);
    goto Start;
  }

  vol=Audio_Volume;
  Buffer=&AudioBuffer[256*2*in];
  if(Samples==NULL){
    for(i=0;i<256;i++){
      Buffer[i*2]=0;
      Buffer[i*2+1]=0;
    }
  }else{
    for(i=0;i<256;i++){
      Buffer[i*2]=((Hint32)((Hint16*)Samples)[i])<<vol;
      Buffer[i*2+1]=((Hint32)((Hint16*)Samples)[i])<<vol;
    }
  }


  if(in==(vAudioBuffer_Cnt-1)){
    InIndex=0;
  }else{
    InIndex=in+1;
  }

}

volatile Huint16 StandardControllerStatus=0U;

static Huint16 GetStandardControllerStatus(void* v){
  

  return StandardControllerStatus;
}

static int FPS_Thread(void* v){
    
  Hint32 t;
  Hint32 FrameRate;
  Hint32 LastFrame;

  t=0;

  for(;;){
    H_TS_ThreadSleep(5);
    if((Time_GetTimeMs()-t)>999){
      t+=1000;

      FrameRate=NES_Frame-LastFrame;
      LastFrame+=FrameRate;

      H_Printf("FPS:{i}\r\n",FrameRate);
    }
  }
  
}

extern const unsigned char supermario_rom[];
extern const unsigned char topgun[];

extern const unsigned char topgun2[];
extern const unsigned char rzljz3_rom[];
extern const unsigned char lkr6_rom[];
extern const unsigned char lkr3_rom[];
extern const unsigned char kickmaster[];

#define vGames_Address (0x90000000U+256U*1024U)

static void* NextGame;

static void* GetGame(){
  Hbyte* r;
  Hint32 length;

  r=NextGame;

  ((Hbyte*)&length)[0]=r[0];
  ((Hbyte*)&length)[1]=r[1];
  ((Hbyte*)&length)[2]=r[2];
  ((Hbyte*)&length)[3]=r[3];

  if(length>0){
    NextGame=&r[4+length];
  }else{
    NextGame=(void*)vGames_Address;
  }

  return &r[4];
}

static void StartGame(){
  H_TS_Thread* thread;
  int r;

  r=H_NES_LoadROM(nes.nes,GetGame());
  if(r==0){
    nes.IsLoad=-1;
    thread=H_TS_StartNewThread(nes_thread,&nes,1024,3,0);
    thread->ThreadName="nes_thread";
    H_NES_WaitUntilRun(nes.nes);
    
  }else{
    H_Printf("\r\nnes:rom加载失败:{i}\r\n",r);
  }
}

static void StopGame(){
  if(nes.IsLoad!=0){
    nes.IsLoad=0;
    H_NES_UnloadROM(nes.nes);
  }
  H_NES_Stop(nes.nes);
}

static int Progress_IsValid;

Local_IRAM_AXI static Hbyte Progress[64*1024];


static void Next(){

  StopGame();
  Progress_IsValid=0;
  StartGame();
}

static void Load(){
  int r;
  if(Progress_IsValid!=0){
    r=H_NES_LoadProgress(nes.nes,Progress);
    if(r!=vH_NES_Result_OK){
      H_Printf("\r\nnes:加载存档时出现错误:{i}\r\n",r);
    }
  }else{
    H_Printf("\r\nnes:无有效的存档\r\n");
  }
}

static void Save(){
  int r;
  if(H_NES_GetProgressSize(nes.nes)>sizeof(Progress)){
    H_Printf("\r\nnes:内存不足以容纳存档\r\n");
  }else{
    r=H_NES_GetProgress(nes.nes,Progress);
    if(r==vH_NES_Result_OK){
      Progress_IsValid=-1;
    }else{
      Progress_IsValid=0;
      H_Printf("\r\nnes:存档时出现错误:{i}\r\n",r);
    }
  }
}

static void EventCallback(void* v,Huint32 Event){
  

  switch(Event&vH_Key_Event_KeyID){
    case vButton_B1:
      if((Event&vH_Key_Event_Up)!=0U){
        //Next
        Next();
      }
      break;
    case vButton_B2:
      if((Event&vH_Key_Event_Up)!=0U){
        
      }
      break;
    case vButton_B3:
      if((Event&vH_Key_Event_Up)!=0U){
        //Load
        Load();
      }
      break;
    case vButton_B4:
      if((Event&vH_Key_Event_Up)!=0U){
        //Save
        Save();
      }
      break;
    default:
      break;
  }
}

static int Init(void* v){
  H_NES_LL_Function LL_Function;
  H_TS_Thread* thread;
  unsigned char cmd;
  int r;
  Hint32 VblankNMI_Delay;
  Hint32 MMC4_CountDelay;

  extern unsigned char NESPalette_FCEUX[];
  
  for(r=0;r<64;r++){
    Palette[r]=(((Huint16)NESPalette_FCEUX[r*3]<<8)&0xF800U)
              |(((Huint16)NESPalette_FCEUX[r*3+1]<<3)&0x07E0U)
              |(((Huint16)NESPalette_FCEUX[r*3+2]>>3)&0x001FU);
    // H_Printf("0x{X4}U,\r\n"
    // ,(((Huint16)Palette_Default[r*3]<<8)&0xF800U)
    // |(((Huint16)Palette_Default[r*3+1]<<3)&0x07E0U)
    // |(((Huint16)Palette_Default[r*3+2]>>3)&0x001FU)
    // );
  }

  for(r=0;r<(sizeof(AudioBufferEmpty)/sizeof(Hint32));r++){
    AudioBufferEmpty[r]=0;
  }
  InIndex=0;
  OutIndex=0;
  Sem_AudioBufferEmpty=new_H_TS_BinarySemaphore();

  I_Screen_ScreenInit();
  I_Screen_SetLight(50);
  ST7789_SetDisplayArea(Screen_ST7789_SPI,0,0,256,240);

  LL_Function.v=&nes;
  LL_Function.ExitCallback=ExitCallback;
  LL_Function.Thread.new_MessageQueue=new_MessageQueue;
  LL_Function.Thread.delete_MessageQueue=delete_MessageQueue;
  LL_Function.Thread.MessageSned=MessageSned;
  LL_Function.Thread.MessageReceive=MessageReceive;
  LL_Function.Thread.MessageTryReceive=MessageTryReceive;
  LL_Function.Thread.ThreadExit=H_TS_ThreadExit;
  LL_Function.Console.RendersLine=RendersLine;
  LL_Function.Console.AudioOutput=AudioOutput;
  LL_Function.Console.GetStandardControllerStatus=GetStandardControllerStatus;
  nes.nes=new_H_NES_Console(&LL_Function,MemoryMalloc_DTCM.Malloc,MemoryMalloc_DTCM.Free);
  if(cH_IsNull(nes.nes)){
    H_Printf("\r\nnes:主机分配失败\r\n");
    return -1;
  }

  if(oIP_GPIO_GetInputBit(GPIOA,1)==0U){
    H_NES_SetTelevisionStandards(nes.nes,vH_NES_TelevisionStandards_NTSC);
  }else if(oIP_GPIO_GetInputBit(GPIOA,2)==0U){
    H_NES_SetTelevisionStandards(nes.nes,vH_NES_TelevisionStandards_Dendy);
  }else{
    H_NES_SetTelevisionStandards(nes.nes,vH_NES_TelevisionStandards_PAL);
  }
  
  
  
  Dbg_TestConsole=nes.nes;
  Progress_IsValid=0;


  IP_SAI_MasterTx_SetMCLKDIV(ip_sai2_b,8U);
  IP_SAI_MasterTx_Start(ip_sai2_b,256*4*2,Audio_GetBuffer_Callback,NULL);

  IsOdd=0;
  DisplayDone=new_H_TS_BinarySemaphore();
  DisplayQueue=new_H_TS_MessageQueue(3);
  H_TS_BinarySemaphoreGive(DisplayDone);//预置1个信号量
  thread=H_TS_StartNewThread(DisplayThread,&nes,1024,2,0);
  thread->ThreadName="DisplayThread";
  thread=H_TS_StartNewThread(FPS_Thread,&nes,1024,3,0);
  thread->ThreadName="FPS_Thread";


  I_Button_Init(EventCallback,NULL);



  NextGame=(void*)vGames_Address;
  VblankNMI_Delay=20;
  MMC4_CountDelay=16;
  H_NES_SetVblankNMI_Delay(nes.nes,VblankNMI_Delay);
  H_NES_SetMMC4_CountDelay(nes.nes,MMC4_CountDelay);
  StartGame();
  for(;;){
    H_TS_ThreadSleep(8);
    I_Button_Loop();

    if(0==H_TS_MailboxTryReceive(nes.Command,&cmd)){

      switch(cmd){
        case 'N':
          Next();
          break;
        case 'S':
          Save();
          break;
        case 'L':
          Load();
          break;
        case '0':
          if(VblankNMI_Delay<1024){
            VblankNMI_Delay++;
          }
          r=H_NES_SetVblankNMI_Delay(nes.nes,VblankNMI_Delay);
          if(r!=vH_NES_Result_OK){
            H_Printf("\r\nVblankNMI_Delay设置失败 Err:{i}\r\n",r);
          }else{
            H_Printf("\r\nVblankNMI_Delay已设置为:{i}\r\n",VblankNMI_Delay);
          }
          break;
        case '1':
          if(VblankNMI_Delay>0){
            VblankNMI_Delay--;
          }
          r=H_NES_SetVblankNMI_Delay(nes.nes,VblankNMI_Delay);
          if(r!=vH_NES_Result_OK){
            H_Printf("\r\nVblankNMI_Delay设置失败 Err:{i}\r\n",r);
          }else{
            H_Printf("\r\nVblankNMI_Delay已设置为:{i}\r\n",VblankNMI_Delay);
          }
          break;
        case '2':
          if(MMC4_CountDelay<1024){
            MMC4_CountDelay++;
          }
          r=H_NES_SetMMC4_CountDelay(nes.nes,MMC4_CountDelay);
          if(r!=vH_NES_Result_OK){
            H_Printf("\r\nMMC4_CountDelay设置失败 Err:{i}\r\n",r);
          }else{
            H_Printf("\r\nMMC4_CountDelay已设置为:{i}\r\n",MMC4_CountDelay);
          }
          break;
        case '3':
          if(MMC4_CountDelay>0){
            MMC4_CountDelay--;
          }
          r=H_NES_SetMMC4_CountDelay(nes.nes,MMC4_CountDelay);
          if(r!=vH_NES_Result_OK){
            H_Printf("\r\nMMC4_CountDelay设置失败 Err:{i}\r\n",r);
          }else{
            H_Printf("\r\nMMC4_CountDelay已设置为:{i}\r\n",MMC4_CountDelay);
          }
          break;
        default:
          break;
      }
    }
  }
  

}






/**
 * @brief 初始化NES
 * @return 无
 */
void nes_Init(){
  H_TS_Thread* thread;

  nes.Command=new_H_TS_Mailbox(8,1,1);

  thread=H_TS_StartNewThread(Init,&nes,1024,1,0);
  thread->ThreadName="nes_Init_thread";

}

void nes_cmd(unsigned char cmd){
  H_TS_MailboxPost(nes.Command,&cmd);
}


